package com.zx.zxpicture.shared.auth;

import cn.dev33.satoken.stp.StpInterface;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.json.JSONUtil;
import com.zx.zxpicture.infrstructure.common.ErrorCode;
import com.zx.zxpicture.infrstructure.exception.BusinessException;
import com.zx.zxpicture.shared.auth.model.SpaceUserAuthContext;
import com.zx.zxpicture.shared.auth.model.SpaceUserPermissionConstant;
import com.zx.zxpicture.domain.picture.entity.Picture;
import com.zx.zxpicture.domain.space.entity.Space;
import com.zx.zxpicture.domain.space.entity.SpaceUser;
import com.zx.zxpicture.domain.user.entity.User;
import com.zx.zxpicture.domain.space.valueObject.SpaceRoleEnum;
import com.zx.zxpicture.domain.space.valueObject.SpaceTypeEnum;
import com.zx.zxpicture.application.service.PictureApplicationService;
import com.zx.zxpicture.application.service.SpaceApplicationService;
import com.zx.zxpicture.application.service.SpaceUserApplicationService;
import com.zx.zxpicture.application.service.UserApplicationService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

import static com.zx.zxpicture.domain.user.objectvalue.UserConstant.USER_LOGIN_STATE;

/**
 * 自定义权限加载接口实现类
 */
@Component    // 保证此类被 SpringBoot 扫描，完成 Sa-Token 的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface {

    @Value("${server.servlet.context-path}")
    private String contextPath;
    @Resource
    private SpaceUserAuthManager spaceUserAuthManager;
    @Resource
    private SpaceUserApplicationService spaceUserApplicationService;
    @Resource
    private SpaceApplicationService spaceApplicationService;
    @Resource
    private PictureApplicationService pictureApplicationService;
    @Resource
    private UserApplicationService userApplicationService;

    /**
     * 返回一个账号所拥有的权限码集合
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        //判断loginType，当前账号体系为“Space”才进行校验
        if (!loginType.equals("space")) {
            //无权限
            return new ArrayList<>();
        }
        //管理员权限，表示放行
        List<String> ADMIN_PERMISSIONS = spaceUserAuthManager.getPermissionsByRole(SpaceRoleEnum.ADMIN.getValue());
        //获取上下文
        SpaceUserAuthContext authContext = getSpaceAuthContextByRequest();
        //如果所有字段都为空，查询公共图库，返回管理员权限
        if (isAllFieldsNull(authContext)) {
            return ADMIN_PERMISSIONS;
        }
        //获取userid
        User loginUser = (User) StpKit.SPACE.getSessionByLoginId(loginId).get(USER_LOGIN_STATE);
        if (ObjUtil.isNull(loginUser)) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
        }
        Long userId = loginUser.getId();
        SpaceUser spaceUser;
        spaceUser = authContext.getSpaceUser();
        if (spaceUser != null) {
            //返回这个空间用户对应权限列表
            return spaceUserAuthManager.getPermissionsByRole(spaceUser.getSpaceRole());
        }
        //当前用户在操作一个团队空间中的成员
        Long spaceUserId = authContext.getSpaceUserId();
        if (spaceUserId != null) {
            spaceUser = spaceUserApplicationService.getById(spaceUserId);
            if (spaceUser == null) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "没有查找到团队空间成员记录");
            }
            //查找登录用户在当前空间的权限列表
            spaceUser = spaceUserApplicationService.lambdaQuery()
                    .eq(SpaceUser::getUserId, userId)
                    .eq(SpaceUser::getSpaceId, spaceUser.getSpaceId())
                    .one();
            if (spaceUser == null) {
                //说明用户不在这个团队空间中
                return new ArrayList<>();
            }
            //
            return spaceUserAuthManager.getPermissionsByRole(spaceUser.getSpaceRole());
        }
        //如果有spaceId，查询当前空间是否是私有空间
        Long spaceId;
        spaceId = authContext.getSpaceId();
        if (spaceId == null) {
            //如果没有spaceId，通过pictureId查找到Space
            Long pictureId = authContext.getPictureId();
            //如果pictureId也没有，默认是公共图库
            if (pictureId == null) {
                return ADMIN_PERMISSIONS;
            }
            Picture picture = pictureApplicationService.lambdaQuery()
                    .eq(Picture::getId, pictureId)
                    .select(Picture::getId, Picture::getSpaceId, Picture::getUserId)
                    .one();
            if (picture == null) {
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "没查找到图片信息");
            }
            spaceId = picture.getSpaceId();
            //公共图库中的图片
            if (spaceId == null) {
                if (picture.getUserId().equals(userId) || userApplicationService.isAdmin(loginUser)) {
                    return ADMIN_PERMISSIONS;
                } else {
                    // 不是自己的图片，仅可查看
                    return Collections.singletonList(SpaceUserPermissionConstant.PICTURE_VIEW);
                }
            }
        }
        //spaceId不为空，说明要操作空间，判断空间是私有还是团队空间
        Space space = spaceApplicationService.getById(spaceId);
        if (space == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "没查找到空间信息");
        }
        //私有空间
        if (space.getSpaceType() == SpaceTypeEnum.PRIVATE.getValue()) {
            if (space.getUserId().equals(userId) || userApplicationService.isAdmin(loginUser)) {
                return ADMIN_PERMISSIONS;
            } else {
                // 不是自己的图片，无权限
                return new ArrayList<>();
            }
        }
        //团队空间
        spaceUser = spaceUserApplicationService.lambdaQuery()
                .eq(SpaceUser::getSpaceId, spaceId)
                .eq(SpaceUser::getUserId, userId)
                .one();
        if(spaceUser == null){
            return new ArrayList<>();
        }
        //返回该用户在团队空间中的权限列表
        return spaceUserAuthManager.getPermissionsByRole(spaceUser.getSpaceRole());
    }

    /**
     * 通过反射获取到这个对象所有属性的值判断是否全为空
     * @param object
     * @return
     */
    private boolean isAllFieldsNull(Object object) {
        if (object == null) {
            return true; // 对象本身为空
        }
        // 获取所有字段并判断是否所有字段都为空
        return Arrays.stream(ReflectUtil.getFields(object.getClass()))
                // 获取字段值
                .map(field -> ReflectUtil.getFieldValue(object, field))
                // 检查是否所有字段都为空
                .allMatch(ObjectUtil::isEmpty);
    }

    /**
     * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        return new ArrayList<>();
    }

    /**
     * 从httpRequest请求中获取上下文对象
     *
     * @return
     */
    public SpaceUserAuthContext getSpaceAuthContextByRequest() {
        //获取httpRequest
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        //根据请求体中的数据类型判断是POST请求还是GET请求
        String contentType = request.getHeader(Header.CONTENT_TYPE.getValue());
        SpaceUserAuthContext authRequest;
        //兼容GET和POST操作
        if (ContentType.JSON.getValue().equals(contentType)) {
            //post
            String body = ServletUtil.getBody(request);
            //只需要id，所以直接封装
            authRequest = JSONUtil.toBean(body, SpaceUserAuthContext.class);
        } else {
            //get请求的参数要用getParamMap来接收
            Map<String, String> paramMap = ServletUtil.getParamMap(request);
            authRequest = BeanUtil.toBean(paramMap, SpaceUserAuthContext.class);
        }
        //根据请求路径来区别id字段的含义
        Long id = authRequest.getId();
        if (ObjUtil.isNotNull(id)) {
            // uri = /api/picture/update
            String uri = request.getRequestURI();
            // partUri = picture/update
            String partUri = uri.replace(contextPath + "/", "");
            // moduleName = picture
            String moduleName = StrUtil.subBefore(partUri, "/", false);
            switch (moduleName) {
                case "picture":
                    authRequest.setPictureId(id);
                    break;
                case "space":
                    authRequest.setSpaceId(id);
                    break;
                case "spaceUser":
                    authRequest.setSpaceUserId(id);
                    break;
                default:
                    break;
            }
        }
        return authRequest;
    }
}
